package com.best.core.utils;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

/**
 * @description: redis工具类
 */
@Component
public class RedisUtil {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //锁名称
    public static final String LOCK_PREFIX = "redis_lock_";
    //加锁失效时间，毫秒
    public static final int LOCK_EXPIRE = 3000; // ms

    /**
     * @description: redis锁,不指定超时时长
     * @Param: [key]
     * @return: boolean
     * @author: 李东
     * @time: 2020-7-6 14:03
     **/
    public boolean lock(String key){
        String lock = LOCK_PREFIX + key;
        // 利用lambda表达式
        return (Boolean) stringRedisTemplate.execute((RedisCallback) connection -> {
            long expireAt = System.currentTimeMillis() + LOCK_EXPIRE + 1;
            Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
            if (acquire) {
                return true;
            } else {
                byte[] value = connection.get(lock.getBytes());
                if (Objects.nonNull(value) && value.length > 0) {
                    long expireTime = Long.parseLong(new String(value));
                    // 如果锁已经过期
                    if (expireTime < System.currentTimeMillis()) {
                        // 重新加锁，防止死锁
                        byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes());
                        return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
                    }
                }
            }
            return false;
        });
    }


    /**
     * @description: redis锁,指定超时时长
     * @Param: [key, timeOut ：超时时长毫秒]
     * @return: boolean
     * @author: 李东
     * @time: 2020-7-1 17:52
     **/
    public boolean lock(String key,long timeOut){
        String lock = LOCK_PREFIX + key;
        // 利用lambda表达式
        return (Boolean) stringRedisTemplate.execute((RedisCallback) connection -> {
            long expireAt = System.currentTimeMillis() + timeOut + 1;
            Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
            if (acquire) {
                return true;
            } else {
                byte[] value = connection.get(lock.getBytes());
                if (Objects.nonNull(value) && value.length > 0) {
                    long expireTime = Long.parseLong(new String(value));
                    // 如果锁已经过期
                    if (expireTime < System.currentTimeMillis()) {
                        // 重新加锁，防止死锁
                        byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes());
                        return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
                    }
                }
            }
            return false;
        });
    }

    /**
     * @description: 解锁
     * @Param: [key]
     * @return: void
     * @author: 李东
     * @time: 2020-7-6 14:02
     **/
    public void unLock(String key) {
        stringRedisTemplate.delete(LOCK_PREFIX+key);
    }


    /**
     * 存放string类型
     *
     * @param key key
     * @param data 数据
     * @param timeout 超时间
     */
    public void setString(String key, String data, Long timeout) {
        stringRedisTemplate.opsForValue().set(key, data);
        if (timeout != null) {
            stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
    }

    /**
     * 存放string类型
     *
     * @param key key
     * @param data 数据
     */
    public void setString(String key, String data) {
        setString(key, data, null);
    }

    /**
     * 根据key查询string类型
     *
     * @param key
     * @return
     */
    public String getString(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        return value;
    }

    /**
     * 根据对应的key删除key
     *
     * @param key
     */
    public boolean delKey(String key) {
        return stringRedisTemplate.delete(key);
    }

    /**
     * 获取redisTemplate对象
     * 
     * @return
     */
    public StringRedisTemplate getStringRedisTemplate() {
        return stringRedisTemplate;
    }


    /**
     * 开启redis事务
     */
    public void begin() {
        // 开启redis事务权限
        stringRedisTemplate.setEnableTransactionSupport(true);
        // 开启事务
        stringRedisTemplate.multi();
    }

    /**
     * 提交事务
     */
    public void commit() {
        stringRedisTemplate.exec();
    }

    /**
     * 回滚事务
     */
    public void rollback() {
        stringRedisTemplate.discard();
    }
}
